package it.crosato.stage.server.model.retriever;

import it.crosato.stage.shared.exceptions.DatabaseConnectionException;
import it.crosato.stage.shared.exceptions.RetrievingException;
import it.crosato.stage.shared.objects.EntityDefinition;

import java.rmi.RemoteException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Date;
import java.util.Vector;
import java.util.logging.Logger;

import javax.xml.rpc.ServiceException;

import keggapi.Definition;
import keggapi.KEGGLocator;
import keggapi.KEGGPortType;

public class PathwaysListRetriever implements IPathwaysListRetriever {
	
	/**
	 * Recupera la lista delle vie metaboliche disponibili gestendo internamente
	 * le interazioni con la banca dati interna e KEGG
	 * @param time tempo di scadenza nella banca dati interna
	 * @return lista delle definizioni delle vie metaboliche disponibili
	 * @throws RetrievingException se si verificano problemi nel recupero
	 */
	@Override
	public Vector<EntityDefinition> getPathwaysList(long time) throws RetrievingException {
		Logger.getLogger("myLog").info("recupero lista vie metaboliche");
		try {
			Vector<EntityDefinition> pathways = searchInDB(time);
			if (pathways == null) {
				pathways = searchInKEGG();
				storeInDB(pathways);
			}
			Utilities.sortDefinitionsByDescription(pathways);
			return pathways;
		}
		catch (DatabaseConnectionException dbce) {
			Logger.getLogger("myLog").info("dbce ---> " + dbce.getMessage());
			throw new RetrievingException(RetrievingException.PATHWAYS_LIST);
		}
		catch (SQLException sqle) {
			Logger.getLogger("myLog").info("sqle ---> " + sqle.getMessage());
			throw new RetrievingException(RetrievingException.PATHWAYS_LIST);
		}
		catch (ServiceException se) {
			Logger.getLogger("myLog").info("se ---> " + se.getMessage());
			throw new RetrievingException(RetrievingException.PATHWAYS_LIST);
		}
		catch (RemoteException re) {
			Logger.getLogger("myLog").info("re ---> " + re.getMessage());
			throw new RetrievingException(RetrievingException.PATHWAYS_LIST);
		}
		finally {
			DatabaseConnection.closeConnection();
		}
	}

	/**
	 * Cerca la lista delle vie metaboliche disponibili nella banca dati interna.
	 * @param time tempo di scadenza nella banca dati interna
	 * @return la lista delle definizioni delle vie metaboliche disponibili oppure il 
	 * valore nullo se la lista non è presente o è troppo vecchia
	 * @throws SQLException se si verificano problemi con le query
	 * @throws DatabaseConnectionException se si verificano problemi con la
	 * connessione alla banca dati
	 */
	private Vector<EntityDefinition> searchInDB(long time) throws SQLException, DatabaseConnectionException {
		
		DatabaseConnection.openConnection();
		Statement st = DatabaseConnection.getStatement();
		ResultSet rs = st.executeQuery("SELECT count(*) from PathwaysList;");
		rs.next();
		if (rs.getInt(1)!=0) {
			Logger.getLogger("myLog").info("vie metaboliche presenti nel database");
			Vector<EntityDefinition> pathwaysList = new Vector<EntityDefinition>();
			rs = st.executeQuery("SELECT * FROM PathwaysList;");
			int i = 0;
			while (rs.next()) {
				Long date = rs.getLong("storeDate");
				if (date + time < new Date().getTime()) {
					Logger.getLogger("myLog").info("lista di vie metaboliche troppo vecchia.");
					return null;
				}
				String id = rs.getString("id");
				String description = rs.getString("description");
				pathwaysList.add(new EntityDefinition(id, description));
				i++;
			}
			Logger.getLogger("myLog").info("recuperate vie metaboliche dal database");
			return pathwaysList;
		}
		else {
			Logger.getLogger("myLog").info("vie metaboliche non presenti nel database");
			return null;
		}
	}

	/**
	 * Cerca la lista delle vie metaboliche disponibili in KEGG
	 * @return la lista delle difinizioni delle vie metaboliche.
	 * @throws RemoteException se si verifica no problemi nell'interazione remota
	 * con KEGG
	 * @throws ServiceException se si verificano problemi legati ai servizi di KEGG
	 */
	private Vector<EntityDefinition> searchInKEGG() throws RemoteException, ServiceException {

		Logger.getLogger("myLog").info("recupero vie metaboliche da KEGG");
		KEGGLocator locator = new KEGGLocator();
		KEGGPortType serv = locator.getKEGGPort();
		Definition[] pathwaysList = serv.list_pathways("map");
		for (int i = 0; i < pathwaysList.length; i++) {
			String def = pathwaysList[i].getDefinition().replace(" - Reference pathway", "");
			pathwaysList[i].setDefinition(def);
		}
		Logger.getLogger("myLog").info("vie metaboliche recuperate da KEGG");
		return Utilities.convertInED(pathwaysList);
	}

	/**
	 * Memorizza nella banca dati interna la lista dell vie metaboliche recuperata
	 * eventualmente sostituendo versioni precedenti
	 * @param pathwaysList la lista delle vie metaboliche da memorizzare
	 * @throws DatabaseConnectionException se si verificano problemi con la 
	 * connessione alla banca dati
	 */
	private void storeInDB(Vector<EntityDefinition> pathwaysList) throws
				DatabaseConnectionException {

		Logger.getLogger("myLog").info("inserimento vie metaboliche nel database");
		DatabaseConnection.openConnection();
		Statement st = DatabaseConnection.getStatement();
		try {
			try {
				st.executeQuery("DELETE FROM PathwaysList;");
			} catch(SQLException e) { }
			Date date = new Date();
			for (int i = 0; i < pathwaysList.size(); i++) {
				String id = pathwaysList.get(i).getId();
				String description = pathwaysList.get(i).getDescription();
				description = description.replaceAll("'", "''");
				st.executeUpdate("INSERT INTO PathwaysList VALUES('" + id 
						+ "', '" + description + "', '" + date.getTime() + "');");
			}
			Logger.getLogger("myLog").info("inserite vie metaboliche nel database");
		}
		catch (SQLException sqle) {
			Logger.getLogger("myLog").info("sqle ---> " + sqle.getMessage());
		}
	}
}